/* ========================================================================= */
/* ------------------------------------------------------------------------- */
/*!
  \file			ETSheetView.h
  \date			september 2011
  \author		TNick

  \brief		Contains the definition for ETSheetView class


  Copyright (c) 2011, Nicu Tofan
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
 * Neither the name of the copyright holder nor the
   names of its contributors may be used to endorse or promote products
   derived from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL NICU TOFAN BE LIABLE FOR ANY
 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
#ifndef __ETSHEETVIEW_INC__
#define __ETSHEETVIEW_INC__
//
//
//
//
/*  INCLUDES    ------------------------------------------------------------ */

#include	<main.h>

class		ETSubItem;
class		ETI_CplxTxt;
class		ETVpCommand;
class		ETSheet;
class		QSplitter;
class		MdiChild;

#include    <QGraphicsView>
#include    <QDomElement>

/*  INCLUDES    ============================================================ */
//
//
//
//
/*  CLASS    --------------------------------------------------------------- */

/// Widget capable of displaying an ETSheet content
/**
*	\section navig	Navigation
*	The part of the ETSheet that is currently displayed may be altered using
*	<a href="http://doc.qt.nokia.com/latest/qgraphicsview.html#translate">
*	QGraphicsView::translate()</a> to move around with relative distances
*	with regards to current position.
*
*	To change magnifying level, one may use
*	<a href="http://doc.qt.nokia.com/latest/qgraphicsview.html#scale">
*	QGraphicsView::scale()</a> or one
*	of the utility functions zoomIn() and zoomOut(). Rotation of the scene
*	relative to the viewport may be adjusted clockwise with respect to current
*	rotation using
*	<a href="http://doc.qt.nokia.com/latest/qgraphicsview.html#rotate">
*	QGraphicsView::rotate()</a> or one of the utility functions
*	rotateLeft() or rotateRight().
*
*	\section commands	Commands
*	Each view is able to maintain a command independent of other views.
*	Having multiple views looking at a single scene means that there may
*	be multiple commands running against same scene. To address possible
*	issues the view is required to only talk with the scene in "atomic"
*	operations (that can't overlap with operations from other views or
*	started from the code). As such, the (permanent) effect on the scene
*	is only commited when the command finishes and all input from the user
*	has been gathered. Commands may create temporary objects in the scene
*	to show an interactive interface, but are required to limit the use
*	of those as the effect is visible in other views, too.
*
*	Commands are started by creating an instance of a class inherited from
*	ETVpCommand and passing it to startCommand(). While current implementation
*	will always call ETVpCommand::start(), this may change in the future. As a
*	result the command (class) shall only initialise itself when it recieves
*	the ETVpCommand::start(), not in constructor. The class instance needs to
*	be created with new() and the ownership of the pointer is taken by this
*	class (the instance will be deleted() during cancelCommand() call).
*
*	Commands may be canceled by either calling cancelCommand() or
*	acceptCommand(). acceptCommand() will inform the ETVpCommand::accept()
*	function, then will fall back to cancelCommand(). cancelCommand() simply
*	informs the command (class instance) about the event and deletes(!) the
*	class instance.
*
*	While a command is running mouse input and keyboard input is managed by it.
*
*
*	\section sel_support	Current sub-item
*	Each ETSheetView instance keeps it's own currently selected sub-item
*	saved. As a logic result, if more than one views are looking into a
*	ETSheet, more than one items belonging to that ETSheet may be current
*	at the same time, but only one is current in a particular view.
*
*	The current sub-item reveals the element that the user is focusing
*	at any given time and may be NULL (no current item). The user manipulates
*	the current item using keyboard amd mouse.
*
*	setCurrentSubI() is simply informed about the selection change. It does
*	not perform steps to change the apparence of the item. To do so, use
*	makeCurrentSubI().
*
*
*	\section edit_support	Editing
*	Items contained in the ETSheet may be edited using appropriate widgets
*	that are created when the editing starts and destroyed when focus is lost
*	or the user accepts the change.
*
*	Creating the editors is the responsability of the sub-item involved.
*	Once created and parented to an ETSheetView instance, the ownership is
*	taken over by this class via a call to setEditor(). This will properly
*	destroy previously existing editor (if any) and will save the new one.
*	Note that the function assumes that the sub-item being edited is the one
*	stored inside crt_sub_itm, which is true when editing is started by
*	the user.
*
*	One may find if the ETSheetView instance has an editor opened by calling
*	inEditMode(). If true, editor() will return the pointer to that editor; if
*	false, the function returns NULL.
*
*	All events that shall result in editor being closed must call
*	closeEditor(). It enshures proper resource release while the content
*	of the editor is saved back to originating sub-item saved inside
*	crt_sub_itm.
*
*
*	\section save_load	Saving and loading from xml file
*	A set of functions are dedicated to perform these tasks. First, entire
*	tree is saved using the static saveXML(). This will fill the top level
*	node with informations about all splits and viewports that are present in the
*	GUI at that moment. The function makes use of saveXMLViewport() for
*	viewports and saveXMLVpSplit() for splits to recursivelly save the tree.
*
*	Loading viewports from a DOM tree is done using loadXMLViewport() and
*	loadXMLVpSplit(). These functionsalso work recursivelly to recreate entire
*	tree of splits and viewports.
*
*	The format of the file that is saved follows the guidance in
*	<a href="https://code.google.com/p/equtemper/source/browse/trunk/doc/example.xml">
*	example.xml</a> that is located in
*	<a href="https://code.google.com/p/equtemper/source/browse/trunk/doc">doc</a>
*	subfolder.
*
*
*	\section various	Various
*	To make an sub-item properly visible inside this view, use makeSubIVisible().
*	If the complete item needs to be presented, disregarding any sub-item,
*	then makeIVisible() may be used. Both of these functions first look at the
*	size of the text and, if too large or too small, scale factor is adjusted.
*	Second, the location is checked against visible rectangle and, if outside,
*	the item is panned in view. If the view rectangle is not large enough
*	to show the entire entity, then top left corner is preffered. The functions
*	properly take into account the rotation of the view relative to scene.
*
*
*
*
*
*/
class ETSheetView : public QGraphicsView DDHC_INHERIT		{
	DDHC_CLASS

	//
	//
	//
	//
	/*  DEFINITIONS    ----------------------------------------------------- */

	/// data about one split element (may be a viewport or another split)
	struct	SplitEl		{
		bool	b_is_vp;
		union	{
			const ETSheetView *		vp;
			const QSplitter *		split;
		} comp;
	};

	/// structure grouping data about one split and it's content
	struct	SplitData	{
		SplitEl			first;
		SplitEl			sec;
	};


	/*  DEFINITIONS    ===================================================== */
	//
	//
	//
	//
	/*  DATA    ------------------------------------------------------------ */

private:

	/// either a pointer to current editor or NULL if none is visible
	QWidget *			temp_editor;

	/// cached parent of the current sub-item (crt_sub_itm)
	ETI_CplxTxt *		crt_itm;

	/// current sub-item
	ETSubItem *			crt_sub_itm;

	/// currently running command / NULL if none is running
	ETVpCommand *		running_command;

	/// selection is either on the right or on the left side
	bool				b_sel_left;

	/// cache current rotation angle for simplicity
	qreal				rot_ang_to_s;

	/// cache current scale for simplicity
	qreal				scale_to_s;

	/// the translation on X axis
	qreal				trans_x;

	/// the translation on Y axis
	qreal				trans_y;


	/*  DATA    ============================================================ */
	//
	//
	//
	//
	/*  FUNCTIONS    ------------------------------------------------------- */

public:

	/// constructor;
	ETSheetView			(
			ETSheet *		the_scene,
			QWidget *		parent = 0
			);

	/// destructor
	virtual				~ETSheetView		( void );


	/// make the sub-item provided in the argument properly visible
	void				makeSubIVisible		(ETSubItem * trg_si);

	/// make the item provided in the argument properly visible
	void				makeIVisible		(ETI_CplxTxt * trg_si);





	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
	/** @name Current sub-item     */
	//@{
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */


	/// tell which is currenly selected sub-item in this view
	inline ETSubItem *	currentSubI			( void )
	{ return crt_sub_itm; }

	/// tell which is the parent for currenly selected sub-item in this view
	inline ETI_CplxTxt *currentItem			( void )
	{ return crt_itm; }


	/// inform the view that a new current item was decided
	void				setCurrentSubI		(
			ETI_CplxTxt *		new_i, /**< parent of current sub-item; may be
			NULL, in wich case it will be
			searched using new_si */
			ETSubItem *			new_si /**< sub-item to amke current in
			this view */
			);


	/// perform all actions required to make this item current in this view
	void				makeCurrentSubI		(
			ETI_CplxTxt *		new_i, /**< parent of current sub-item; may be
			NULL, in wich case it will be
			searched using new_si */
			ETSubItem *			new_si /**< sub-item to amke current in
			this view */
			);


	/// tells if the sellection appears on the left (true) or on the right (false)
	inline bool			selIsLeft			( void )
	{ return b_sel_left; }


	/// informs the viewport that the selection is on the left side
	inline void			setSelLeft			( void )
	{ b_sel_left = true; }


	/// informs the viewport that the selection is on the right side
	inline void			setSelRight			( void )
	{ b_sel_left = false; }

	/// item provided in argument requests selecting first item to it's left
	void				navLeaveLeft		(ETI_CplxTxt * it_base);

	/// item provided in argument requests selecting first item to it's right
	void				navLeaveRight		(ETI_CplxTxt * it_base);

	/// item provided in argument requests selecting first item above it
	void				navLeaveTop			(ETI_CplxTxt * it_base);

	/// item provided in argument requests selecting first item below i
	void				navLeaveBtm			(ETI_CplxTxt * it_base);


	//@}
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */




	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
	/** @name Editing support     */
	//@{
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
public:


	/// tell if we're in edit mode or not
	inline bool			inEditMode	( void )
	{ return (temp_editor != NULL); }


	/// get current editor if any
	inline QWidget *	editor		( void )
	{ return temp_editor; }


	/// close current editor, if any; the text is not validated
	void				closeEditor	( void );


private:

	/// set current editor
	void				setEditor	(QWidget * new_editor);



	//@}
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */




public:

	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
	/** @name Navigation    */
	//@{
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */


	/// uses provided factor to change the scale, making texts larger
	inline void			zoomIn				(qreal factor = 1.2)
	{ scale_to_s *= factor; scale(factor, factor); }


	/// uses provided factor to change the scale
	inline void			zoomFactor			(qreal factor)
	{ qreal scl_f = factor / scale_to_s; scale(scl_f, scl_f); scale_to_s = factor; }


	/// uses provided factor's inverse (1/n) to change the scale, making texts smaller
	inline void			zoomOut				(qreal factor = 1.2)
	{ scale_to_s /= factor; scale( 1 / factor, 1 / factor); }


	/// chooses best fit so that the points are visible
	void				zoomWin				( QPointF pt_1, QPointF pt_2 );


	/// makes entire content of the scene visible in viewport
	void				zoomExt				( void );


	/// change display angle, using the negative of the argument
	void				rotateLeft			(qreal angle = 10)
	{ rot_ang_to_s -= angle; rotate(-angle); }


	/// change display angle, using the argument
	void				rotateRight			(qreal angle = 10)
	{ rot_ang_to_s += angle; rotate(angle); }


	/// tell the rotation angle of this view to the scene
	qreal				getRotation			( void ) const
	{ return rot_ang_to_s; }


	/// return current scale of this view to the scene
	qreal				getScale			( void ) const
	{ return scale_to_s; }


	/// sneak in to see what's up
	void				translate			( qreal dx, qreal dy )
	{ trans_x+= dx; trans_y += dy; QGraphicsView::translate(dx, dy); }


	/// sneak in to see what's up
	void				translate			( QPointF trans_loc )
	{ QGraphicsView::translate(trans_loc.x() - trans_x, trans_loc.y() - trans_y);
		trans_x = trans_loc.x(); trans_y = trans_loc.y();  }


	//@}
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */



	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
	/** @name Commands interface    */
	//@{
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

	/// tell if we are running a command or we're in idle mode
	inline bool			inCommand			( void )
	{ return (running_command != NULL); }

	/// get a pointer to currently running command / NULL if in idle mode
	inline ETVpCommand *command				( void )
	{ return running_command; }

	/// starts a command; will cancel previous command if running
	void				startCommand		(ETVpCommand * new_cmd);

	/// cancels currently running command
	void				cancelCommand		( void );

	/// stops current running command but ETCommand::accepted() is caled before
	void				acceptCommand		( void );

	//@}
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */



	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
	/** @name Reimplemented events    */
	//@{
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
protected:

	void 	contextMenuEvent		( QContextMenuEvent * event );
	void 	dragEnterEvent			( QDragEnterEvent * event );
	void 	dragLeaveEvent			( QDragLeaveEvent * event );
	void 	dragMoveEvent			( QDragMoveEvent * event );
	void 	dropEvent				( QDropEvent * event );
	void 	keyPressEvent			( QKeyEvent * event );
	void 	keyReleaseEvent			( QKeyEvent * event );
	void 	mouseDoubleClickEvent	( QMouseEvent * event );
	void 	mouseMoveEvent			( QMouseEvent * event );
	void 	mousePressEvent			( QMouseEvent * event );
	void 	mouseReleaseEvent		( QMouseEvent * event );
	void 	wheelEvent				( QWheelEvent * event );

	void	setScene				( QGraphicsScene * scene );

	void	drawBackground			( QPainter * painter, const QRectF & rect );
	//@}
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */




	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
	/** @name Save and load	*/
	//@{
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

public:

	/// creates an viewport in specified sheet based on DOM node
	static ETSheetView *	loadXMLViewport		(
			ETSheet *			dest,
			QWidget *			parent,
			QDomElement			dom_e
			);


	/// creates an split in specified sheet based on DOM node
	static void *			loadXMLVpSplit		(
			ETSheet *			dest,
			QWidget *			parent,
			QDomElement			dom_e
			);


	/// saves the defining elements of the viewports in a sheet to DOM element
	static void				saveXML				(
			ETSheet *			dest,
			QDomElement			dom_e
			);

	/// loads the tree of viewports and splits from DOM element
	static void				loadXML				(
			ETSheet *			dest,
			QWidget *			parent,
			QDomElement			dom_e
			);

	/// saves current viewport in provided DOM element
	void					saveXMLViewport		(
			ETSheet *			dest,
			QDomElement			dom_e
			) const;


	/// saves the split and all it's kids in provided DOM element
	static void				saveXMLVpSplit			(
			ETSheet *			dest,
			const QSplitter *	spl,
			QDomElement			dom_e
			);


	//@}
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */




	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
	/** @name Splitting	management */
	//@{
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
public:


	/// Splits current viewport in two horizontal viewports
	void        splitH          ( void );


	/// Splits current viewport in two vertical viewports
	void        splitV          ( void );


	/// Toogles the orientation of the split between horizontal and vertical
	/**
	*	The split that is toogled is the one hosting current viewport
	*/
	void        splitTgl        ( void );


	/// Splitted viewports are rejoined
	/**
	*	The splitter hosting this viewport is destroyed and it;s place is taken
	*	by this viewport
	*/
	void        rejoinVP		( void );


	/// gets info about the content of a split
	static void	vp_SplitKids		(
			const QWidget *	parent,
			SplitData *		sd
			);

private:


	/// create a top level split in associated MDI
	static void	ivp_SplitTopL      (
			ETSheetView * vp_loc,
			MdiChild * parent,
			Qt::Orientation orient
			);


	/// create a child split in associated MDI
	static void	ivp_SplitSplKid    (
			ETSheetView * vp_loc,
			QWidget * parent,
			Qt::Orientation orient
			);


	//@}
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */










private:

	/// used by various flavours of constructors
	void		init		( void );






	/*  FUNCTIONS    ======================================================= */
	//
	//
	//
	//

};	/*	class ETSheetView	*/

/*  CLASS    =============================================================== */
//
//
//
//
#endif // __ETSHEETVIEW_INC__
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
